#include "Connector.h"
#include "EventLoop.h"
#include "Channel.h"
#include "SocketsOps.h"
#include "../base/logging.h"

#include <functional>
#include <errno.h>
#include <sstream>
#include <iostream>
#include <assert.h>

const int Connector::kMaxRetryDelayMs;

Connector::Connector(EventLoop* loop, const InetAddress& addr)
	: m_pLoop(loop),
	  m_serverAddr(addr),
	  m_state(kDisconnected),
	  m_bConnect(false),
	  m_retryDelayMs(kInitRetryDelayMs)
{
	LOG_DEBUG << "ctor [" << this << "]";
}

Connector::~Connector()
{
	LOG_DEBUG << "dtor [" << this << "]";
}

void Connector::start()
{
	m_bConnect = true;
	m_pLoop->runInLoop(std::bind(&Connector::startInLoop, this));
}

void Connector::restart()
{
	m_pLoop->assertInLoopThread();
	setState(kDisconnected);
	m_retryDelayMs = kInitRetryDelayMs;
	m_bConnect = true;
	startInLoop();
}

void Connector::stop()
{
	m_bConnect = false;
	m_pLoop->runInLoop(std::bind(&Connector::stopInLoop, shared_from_this()));	//FIXME:unsafe
	//FIXME:cancel time;
}

void Connector::stopInLoop()
{
	//std::stringstream ss;	
	//ss << "stopInLoop eventloop threadid = " << std::this_thread::get_id();
	//std::cout << ss.str() << std::endl;

	m_pLoop->assertInLoopThread();
	if (m_state == kConnecting)
	{
		setState(kDisconnected);
		int sockfd = removeAndResetChannel();
		retry(sockfd);
	}
}

void Connector::startInLoop()
{
	m_pLoop->assertInLoopThread();
	assert(m_state == kDisconnected);
	if (m_bConnect)
	{
		connect();
	}
	else
	{
		LOG_DEBUG << " do not connect";
	}
}

void Connector::connect()
{
	int sockfd = sockets::createNonblockingOrDie();
	int ret = sockets::connect(sockfd, m_serverAddr.getSockAddr());
	
	int savedErrno = (ret == 0) ? 0 : errno;

	switch (savedErrno)
	{
	case 0:
	case EINPROGRESS:
	case EINTR:
	case EISCONN:
		connecting(sockfd);
		break;

	case EAGAIN:
	case EADDRINUSE:
	case EADDRNOTAVAIL:
	case ECONNREFUSED:
	case ENETUNREACH:
		retry(sockfd);
		break;

	case EACCES:
	case EPERM:
	case EAFNOSUPPORT:
	case EALREADY:
	case EBADF:
	case EFAULT:
	case ENOTSOCK:
		LOG_SYSERR << "connect error in Connector::startInLoop " << savedErrno;
		sockets::close(sockfd);
		break;

	default:
		LOG_SYSERR << "Unexpected error in Connector::startInLoop " << savedErrno;
		sockets::close(sockfd);
		// connectErrorCallback_();
		break;
	}
}

void Connector::connecting(int sockfd)
{
	setState(kConnecting);
	assert(!m_pChannel);
	m_pChannel.reset(new Channel(m_pLoop, sockfd));
	m_pChannel->setWriteCallback(std::bind(&Connector::handleWrite, this));		//FIXME:unsafe;
	m_pChannel->setErrorCallback(std::bind(&Connector::handleError, this));		//FIXME:unsafe
	m_pChannel->enableWriting();
}

int Connector::removeAndResetChannel()
{
	//关闭所有事件的响应
	m_pChannel->disableAll();
	//注销eventloop上的channel
	m_pChannel->remove();
	int sockfd = m_pChannel->fd();
	// Can't reset channel_ here, because we are inside Channel::handleEvent
	//注销Channel,shared_from_this or this ?
	m_pLoop->queueInLoop(std::bind(&Connector::resetChannel, this));
		//保留fd
	return sockfd;
}

void Connector::resetChannel()
{
	m_pChannel.reset();
}

void Connector::handleWrite()
{
	LOG_TRACE << "Connector::handleWrite " << m_state;
	if (m_state == kConnecting)
	{
		//获取SO_ERROR状态，重置Channel,设置用户的连接函数
		int sockfd = removeAndResetChannel();
		int err = sockets::getSocketError(sockfd);
		if (err)
		{
			LOG_WARN << "Connector::handleWrite() - SO_ERROR : "
				<< err << " " << strerror_tl(err);
			retry(sockfd);
		}
		else if (sockets::isSelfConnect(sockfd))
		{
			LOG_WARN << "Connector::handleWrite - Self connect";
			retry(sockfd);
		}
		else
		{
			setState(kConnected);
			if (m_bConnect)
			{
				//newConnectionCallback_指向TcpClient::newConnection(int sockfd)
				newConnectionCallback_(sockfd);
			}
			else
			{
				sockets::close(sockfd);
			}
		}
	}
	else
	{
		// what happened?
		assert(m_state == kDisconnected);
	}
}

void Connector::handleError()
{

}

void Connector::retry(int sockfd)
{
	sockets::close(sockfd);
	setState(kDisconnected);
	if (m_bConnect)
	{
		LOG_INFO << "Connector::retry - Retry connecting to " << m_serverAddr.toHostPort()
			<< " in " << m_retryDelayMs << " milliseconds. ";
		//一定要shared_from_this?
		m_pLoop->runAfter(m_retryDelayMs / 1000.0,
			std::bind(&Connector::startInLoop, shared_from_this()));
		m_retryDelayMs = std::min(m_retryDelayMs * 2, kMaxRetryDelayMs);
		//定时器重试， todo
	}
	else
	{
		LOG_DEBUG << " do not connect";
	}
}